#include "server.h"

extern MYSQL* mySql;

//为防止一次不能发送完想发送的数据，此处采用Sendn。
int Sendn(int fd, void* buf, int len) {
    char* bufChar = (char*)buf;
    int size = len;
    while(size > 0) {
        int ret = send(fd, bufChar, size, 0);
        if(ret < 0) {
            perror("sendn");
            return len - size;
        }
        size -= ret;
        bufChar += ret;
    }
    return len - size;
}


//客户端下载文件
int SendFile(char* srcName, int peerFd, int usrId) {
    int ret;
    //定义train类型的对象
    Train_t train;
    //先把要传输的文件名从路径中分离出来
    char fileName[1024] = {0};
    char pathName[1024] = {0};
    int length = strlen(srcName) - 1;
    if(length < 0) {
        return -1;
    }
    if(srcName[length] == '/') {
        srcName[length] = 0;
        length--;
    }
    if(length < 0) {
        return -1;
    }
    int i;
    for(i = length; i >= -1; i--) {
        if(i < 0 || srcName[i] == '/') {
            break;
        }
    }
    strcpy(fileName, srcName + i + 1);
    if(i >= 0) {
        memcpy(pathName, srcName, i);
    }
    if(pathName[0] == '\0' || pathName[0] == '.'){//不输路径或输入当前路径，则在家目录下寻找
        pathName[0] = '~';
        pathName[1] = '\0';
    }
    //判断文件是否存在，并设标志位fileExist
    int fileExist = 1;
    int fileId;
    char querySentence[2048] = {0};
    char* md5Name;
    off_t FileSize;
    ret = PathnameAnalyse(pathName, usrId, &fileId);
    if(ret == -1){//路径错误
        printf("path error.\n");
        fileExist = 0;
    }else{//查询数据库，判断文件是否存在，存在则获取其长度及服务器端文件名
        printf("fileId = %d\n", fileId);
        sprintf(querySentence, "select owner_id, filesize, md5 from filesystem where filename = '%s' and parent_id = %d",
                fileName, fileId);
        ret = mysql_query(mySql, querySentence);
        if(ret != 0) {
            printf("query error: %s\n", mysql_error(mySql));
            return -1;
        }
        MYSQL_RES* result = mysql_store_result(mySql);
        if(result == NULL){
            printf("query error: %s\n", mysql_error(mySql));
            return -1;
        }
        MYSQL_ROW row = mysql_fetch_row(result);
        if(row == NULL){
            fileExist = 0;
            printf("no file.\n");
        }else if(usrId == atoi(row[0])){
            FileSize = (off_t)atoi(row[1]);
            md5Name = row[2];
        }else{
            printf("file is not own.\n");
            fileExist = 0;
        }
        //释放结果集
        mysql_free_result(result);
    }

    /*
    DIR* dirp = opendir(pathName);
    if(dirp == NULL) {
        perror("opendir");
        return -1;
    }
    struct dirent* entry;
    while((entry = readdir(dirp)) != NULL) {
        if(!strcmp(entry->d_name, fileName)) {
            fileExist = 1;
            break;
        }
    }
    */
    if(send(peerFd, &fileExist, 4, MSG_WAITALL) < 0){
        perror("send");
        return -1;
    }
    if(fileExist == 0){
        return -1;
    }
    //打开文件
    char trueName[2048];
    sprintf(trueName, "file/%s", md5Name);
    int fd = open(trueName, O_RDWR);
    if(fd == -1) {
        perror("open");
        return -1;
    }
    //发送文件名
    memset(&train, 0, sizeof(train));
    train.lenth = strlen(fileName);
    memcpy(train.buf, fileName, train.lenth);
    Sendn(peerFd, &train, 4 + train.lenth);
    //发送文件大小
    memset(&train, 0, sizeof(Train_t));
    train.lenth = sizeof(off_t);
    memcpy(train.buf, &FileSize, train.lenth);
    Sendn(peerFd, train.buf, train.lenth);
    //接收对方所告知的已传文件大小
    off_t byteHaveSend = 0;
    ret = recv(peerFd, &byteHaveSend, sizeof(off_t), MSG_WAITALL);
    if(ret == -1) {
        perror("recv");
        return -1;
    }else if(ret == 0){
        printf("conn has closed.\n");
        close(peerFd);
        return -1;
    }
    //文件已存在，则不用发送
    if(byteHaveSend == FileSize) {
        printf("quick upload success\n");
        return 0;
    }
    //采用零拷贝发送文件内容
    printf("byteHaveSend: %ld\n", byteHaveSend);
    char* buf = (char*)mmap(NULL, FileSize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
    if(buf == MAP_FAILED) {
        perror("mmap");
        return -1;
    }
    Sendn(peerFd, buf + byteHaveSend, FileSize - byteHaveSend);
    printf("send has completed.\n");
    munmap(buf, FileSize);
    //关闭文件并返回
    close(fd);
    return 0;
}
